﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace iTool.Cloud.Database
{
    public static class iLock
    {
        static Dictionary<string, ReaderWriterLockSlim> lookDic;
        static iLock()
        {
            lookDic = new Dictionary<string, ReaderWriterLockSlim>();
        }

        static ReaderWriterLockSlim GetReaderWriterLock(string key)
        {
            if (lookDic.TryGetValue(key, out ReaderWriterLockSlim readerWriterLock))
            {
                return readerWriterLock;
            }

            lock (lookDic)
            {
                if (lookDic.TryGetValue(key, out readerWriterLock))
                {
                    return readerWriterLock;
                }
                readerWriterLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
                lookDic.Add(key, readerWriterLock);
                return readerWriterLock;
            }
        }

        public static bool IsReadLockHeld(string key)
        {
            return GetReaderWriterLock(key).IsReadLockHeld;
        }

        public static bool IsUpgradeableReadLockHeld(string key)
        {
            return GetReaderWriterLock(key).IsUpgradeableReadLockHeld;
        }

        public static bool IsWriteLockHeld(string key)
        {
            return GetReaderWriterLock(key).IsWriteLockHeld;
        }

        public static bool EnterReadLock(string key, int millisecondsTimeout = 1000 * 60)
        {
            if (IsReadLockHeld(key))
            {
                return true;
            }
            return GetReaderWriterLock(key).TryEnterReadLock(millisecondsTimeout);
        }

        public static bool EnterUpgradeableReadLock(string key, int millisecondsTimeout = 1000 * 60)
        {
            if (IsUpgradeableReadLockHeld(key))
            {
                return true;
            }
            return GetReaderWriterLock(key).TryEnterUpgradeableReadLock(millisecondsTimeout);
        }

        public static bool EnterWriteLock(string key, int millisecondsTimeout = 1000 * 60)
        {
            if (IsWriteLockHeld(key))
            {
                return true;
            }
            return GetReaderWriterLock(key).TryEnterWriteLock(millisecondsTimeout);
        }

        public static void ExitReadLock(string key)
        {
            if (IsReadLockHeld(key))
                GetReaderWriterLock(key).ExitReadLock();
        }

        public static void ExitUpgradeableReadLock(string key)
        {
            if (IsUpgradeableReadLockHeld(key))
                GetReaderWriterLock(key).ExitUpgradeableReadLock();
        }

        public static void ExitWriteLock(string key)
        {
            if (IsWriteLockHeld(key))
                GetReaderWriterLock(key).ExitWriteLock();
        }
    }


    public static class iLock<T>
    {
        static Dictionary<string, ReaderWriterLockSlim> lookDic;
        static iLock() 
        {
            lookDic = new Dictionary<string, ReaderWriterLockSlim>();
        }

        static ReaderWriterLockSlim GetReaderWriterLock(string key) 
        {
            if (lookDic.TryGetValue(key, out ReaderWriterLockSlim readerWriterLock)) {
                return readerWriterLock;
            }

            lock (lookDic)
            {
                if (lookDic.TryGetValue(key, out readerWriterLock))
                {
                    return readerWriterLock;
                }
                readerWriterLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
                lookDic.Add(key, readerWriterLock);
                return readerWriterLock;
            }
        }

        public static bool IsReadLockHeld(string key)
        {
            return GetReaderWriterLock(key).IsReadLockHeld;
        }

        public static bool IsUpgradeableReadLockHeld(string key)
        {
            return GetReaderWriterLock(key).IsUpgradeableReadLockHeld;
        }

        public static bool IsWriteLockHeld(string key)
        {
            return GetReaderWriterLock(key).IsWriteLockHeld;
        }

        public static bool EnterReadLock(string key, int millisecondsTimeout = 1000 * 60)
        {
            //Console.WriteLine(string.Format("EnterReadLock:{0},{1},{2}", key, GetReaderWriterLock(key).CurrentReadCount, IsReadLockHeld(key)));
            if (IsReadLockHeld(key))
            {
                return true;
            }
            return GetReaderWriterLock(key).TryEnterReadLock(millisecondsTimeout);
        }

        public static bool EnterUpgradeableReadLock(string key, int millisecondsTimeout = 1000 * 60)
        {
            //Console.WriteLine(string.Format("EnterUpgradeableReadLock:{0}", key));
            if (IsUpgradeableReadLockHeld(key))
            {
                return true;
            }
            return GetReaderWriterLock(key).TryEnterUpgradeableReadLock(millisecondsTimeout);
        }

        public static bool EnterWriteLock(string key, int millisecondsTimeout = 1000 * 60)
        {
            //Console.WriteLine(string.Format("EnterWriteLock:{0},CurrentReadCount:{1}", key, GetReaderWriterLock(key).CurrentReadCount));
            if (IsWriteLockHeld(key))
            {
                return true;
            }

            try
            {
                return GetReaderWriterLock(key).TryEnterWriteLock(millisecondsTimeout);
            }
            catch (Exception)
            {
                return false;
            }
        }

        public static void ExitReadLock(string key)
        {
            //Console.WriteLine(string.Format("-  ExitReadLock:{0},{1}", key, IsReadLockHeld(key)));
            if (IsReadLockHeld(key))
                GetReaderWriterLock(key).ExitReadLock();
            //Console.WriteLine(string.Format("-- ExitReadLock:{0},{1}", key, GetReaderWriterLock(key).CurrentReadCount));
        }

        public static void ExitUpgradeableReadLock(string key)
        {
            //Console.WriteLine(string.Format("ExitUpgradeableReadLock:{0}", key));
            if (IsUpgradeableReadLockHeld(key))
                GetReaderWriterLock(key).ExitUpgradeableReadLock();
        }

        public static void ExitWriteLock(string key)
        {
            //Console.WriteLine(string.Format("ExitWriteLock:{0},CurrentReadCount:{1}", key, GetReaderWriterLock(key).CurrentReadCount));
            if (IsWriteLockHeld(key))
                GetReaderWriterLock(key).ExitWriteLock();
        }
    }
}
